/**
 * @file AtpgLibrary.hh
 * @author Xiaoze Lin (linxiaoze96@gmail.com)
 * @brief The standard cell model and atpg library parser class.
 * @version 0.1
 *
 * @copyright Copyright (c) 2024
 *
 */

#pragma once

#include <algorithm>
#include <fstream>
#include <iostream>
#include <queue>
#include <string>
#include <vector>

#include "Netlist.hh"
#include "json.hpp"
#include "log/Log.hh"

namespace ifsim {

using Json = nlohmann::json;

class StdCellModel
{
 public:
  StdCellModel() { ; }
  ~StdCellModel() { ; }

  void cellName(const std::string name) { _cellName.push_back(name); }
  const std::vector<std::string>& cellName() const { return _cellName; }

  void cellInputs(std::string inputName) { _inputs.push_back(inputName); }
  const std::vector<std::string>& cellInputs() const { return _inputs; }
  const std::string& cellInputs(int i) const { return _inputs[i]; }

  void cellInputNodes(int num) { _inputNodes.push_back(num); }
  const std::vector<int>& cellInputNodes() const { return _inputNodes; }

  void cellInputNum(const int num) { _inputNum = num; }
  const int cellInputNum() const { return _inputNum; }

  void cellOutputs(std::string outputName) { _outputs.push_back(outputName); }
  const std::vector<std::string>& cellOutputs() const { return _outputs; }
  const std::string& cellOutputs(int i) const { return _outputs[i]; }

  void cellOutputNodes(int num) { _outputNodes.push_back(num); }
  const std::vector<int>& cellOutputNodes() const { return _outputNodes; }

  void cellOutputNum(const int num) { _outputNum = num; }
  const int cellOutputNum() const { return _outputNum; }

  void primitiveNum(const int num) { _primitiveNum = num; }
  const int primitiveNum() const { return _primitiveNum; }

  void primitiveNode(const PrimitiveGate& node) { _primitiveNodes[node.gid()] = node; }
  PrimitiveGate primitiveNode(int num) const { return _primitiveNodes[num]; }
  const std::vector<PrimitiveGate>& primitiveNodes() { return _primitiveNodes; }
  void primitiveNodes(std::vector<PrimitiveGate>& pri_nodes) { _primitiveNodes = pri_nodes; }
  void initPrimitiveNode(int num) { _primitiveNodes.resize(num); }
  int wholePrimitiveNum() { return _primitiveNodes.size(); }  // include inputs, pris, outputs

 private:
  std::vector<std::string> _cellName;
  unsigned _inputNum;
  unsigned _outputNum;
  unsigned _primitiveNum;
  std::vector<std::string> _inputs;
  std::vector<int> _inputNodes;
  std::vector<std::string> _outputs;
  std::vector<int> _outputNodes;
  std::vector<PrimitiveGate> _primitiveNodes;
};

class AtpgLibrary
{
 public:
  AtpgLibrary() { ; }
  ~AtpgLibrary() { ; }

  void parserStdCells(const char* library_name);
  void jsonToStdCells(Json* json);
  void insertBufferAfterInputFanoutStem(StdCellModel& cell);
  void insertBufferAfterOutputFanoutStem(StdCellModel& cell);

  StdCellModel* findCell(std::string cell_name);

 private:
  std::string _testbenchFileName;
  std::vector<StdCellModel> _stdCells;
  std::map<std::string, int> _nameMap;  // map the name of standard cell to number
};
}  // namespace ifsim